package com.windaka.elements.service.impl;


import com.alibaba.fastjson.JSON;
import com.google.code.kaptcha.Producer;
import com.windaka.elements.annotation.NullToEmpty;
import com.windaka.elements.dao.SysUserRoleMapper;
import com.windaka.elements.exception.BaseException;
import com.windaka.elements.exception.ResultException;
import com.windaka.elements.model.SysUserRole;
import com.windaka.elements.model.dto.AddSysUserDTO;
import com.windaka.elements.model.dto.LoginDTO;
import com.windaka.elements.model.vo.LoginImageVO;
import com.windaka.elements.model.vo.LoginOauthVO;
import com.windaka.elements.model.vo.LoginVO;
import com.windaka.elements.type.UserAccountStatusType;
import com.windaka.elements.utils.CheckParamUtils;
import com.windaka.elements.service.OauthService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.client.fluent.Content;
import org.apache.http.client.fluent.Request;
import org.apache.http.message.BasicNameValuePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.web.HttpRequestMethodNotSupportedException;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * OauthService
 * @author song
 * @date 19:27 2022/8/17
**/
@Slf4j
@Service
@RequiredArgsConstructor
public class OauthServiceImpl implements OauthService {

    private final SysUserRoleMapper sysUserRoleMapper;

    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private TokenEndpoint tokenEndpoint;

    @Resource(name = "captchaProducer")
    private Producer captchaProducer;

    @Value("${security-ip}")
    private String securityIp;

    @Value("${security-port}")
    private String securityPort;

    @Value("${client-id}")
    private String clientId;

    @Value("${client-secret}")
    private String clientSecret;

    @Value("${grant-type}")
    private String grantType;

    /**
     * 登录
     * @author song
     * @date 19:26 2022/8/17
     * @param loginDTO 登录参数
     * @return LoginVO 返回信息
     **/
    @NullToEmpty
    @Override
    public LoginVO login1(LoginDTO loginDTO) throws BaseException {
        String oauthUrl = "http://" + securityIp + ":" + securityPort + "/oauth/token";
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();
        LoginVO loginVO = new LoginVO();
        try {
            Content content = Request.Post(oauthUrl)
                    .bodyForm(oauthBodyBuilder(username, password))
                    .execute()
                    .returnContent();
            LoginOauthVO loginOauthVO = JSON.parseObject(content.asString(StandardCharsets.UTF_8), LoginOauthVO.class);
            loginVO.setToken(loginOauthVO.getAccess_token());
            loginVO.setUsername(loginDTO.getUsername());
            return loginVO;
        } catch (IOException e) {
            log.error("[OauthServiceImpl login => error ! {}]", e.getMessage() + " " + e.getCause());
            throw new ResultException("认证失败: 用户名或密码错误! " + e.getMessage());
        }
    }

    /**
     * 登录
     * @author song
     * @date 2025/6/16 10:13
     * @param loginDTO 登录参数
     * @return LoginVO 返回信息
     * @throws BaseException 基础异常
     */
    @NullToEmpty
    @Override
    public LoginVO login(LoginDTO loginDTO) throws BaseException {
        // 验证码校验
//        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
//        if (!loginDTO.getImageCode().equals(valueOperations.get(loginDTO.getUuid()))) {
//            throw new ResultException("验证码错误");
//        }
        // 密码账号校验
        Principal principal = new UsernamePasswordAuthenticationToken(clientId, clientSecret,
                Collections.emptyList());
        OAuth2AccessToken token = null;
        try {
            token = tokenEndpoint.postAccessToken(principal, oauthParameters(loginDTO.getUsername(), loginDTO.getPassword())).getBody();
        } catch (HttpRequestMethodNotSupportedException e) {
            log.error("[OauthServiceImpl login => error ! {}]", e.getMessage() + " " + e.getCause());
            throw new ResultException("认证失败: 用户名或密码错误! " + e.getMessage());
        }
        if (token == null) {
            throw new ResultException("认证失败: token为空!");
        }

        // 获取用户角色ID
        SysUserRole sysUserRole = sysUserRoleMapper.selectOneUserRole(SysUserRole
                .builder()
                .username(loginDTO.getUsername())
                .build());
        // 判断状态是否禁用
        if(CheckParamUtils.isNotNullAndNotEmpty(sysUserRole.getUserStatus()) &&
            sysUserRole.getUserStatus().equals(UserAccountStatusType.DISABLE.key)) {
            throw new ResultException("登录失败: 用户已被禁用!");
        }
        return LoginVO
                .builder()
                .username(loginDTO.getUsername())
                .token(token.getValue())
                .roleId(sysUserRole.getRoleId())
                .build();
    }

    /**
     * 构造远程认证服务器 请求
     * @author song
     * @date 19:38 2022/8/17
     * @param username 用户名
     * @param password 密码
     * @return List<?> 构造的结果
     **/
    private List<NameValuePair> oauthBodyBuilder(String username, String password) {
        List<NameValuePair> postData = new ArrayList<>();
        postData.add(new BasicNameValuePair("username", username));
        postData.add(new BasicNameValuePair("password", password));
        postData.add(new BasicNameValuePair("grant_type", grantType));
        postData.add(new BasicNameValuePair("client_id", clientId));
        postData.add(new BasicNameValuePair("client_secret", clientSecret));
        return postData;
    }

    private Map<String, String> oauthParameters(String username, String password) {
        Map<String, String> parameters = new HashMap<>();
        parameters.put("grant_type", "password");
        parameters.put("username", username);
        parameters.put("password", password);
        parameters.put("client_id", clientId);
        parameters.put("client_secret", clientSecret);
        return parameters;
    }

    /**
     * 获取验证码
     * @author song
     * @date 2025/6/15 17:54
     * @return LoginImageVO
     */
    @Override
    public LoginImageVO getImageCode() throws BaseException {
        LoginImageVO result = new LoginImageVO();
        UUID uuid = UUID.randomUUID();
        result.setUuid(uuid.toString());
        StringBuilder stringBuilder=new StringBuilder();
        Random random=new Random();
        //随机生成一个四位数
        for (int i = 0; i < 4; i++) {
            stringBuilder.append(random.nextInt(10));
        }
        //将随机生成的四位数缓存到redis中，生成相应的时间
        redisTemplate.opsForValue().set(String.valueOf(uuid), stringBuilder.toString(), 60, TimeUnit.SECONDS);
        BufferedImage image = null;
        //生成的字符串调用kaptcha组件生成图片
        image = captchaProducer.createImage(stringBuilder.toString());
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
        try{
            ImageIO.write(image, "jpg", os);
            result.setImage(Base64Utils.encodeToString(os.toByteArray()));
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 添加用户
     * @author song
     * @date 2025/6/15 18:31
     * @param addSysUserDTO 添加用户参数
     * @return String
     * @throws BaseException 基础异常
     */
    @Override
    public String addUser(AddSysUserDTO addSysUserDTO) throws BaseException {

        return null;
    }
}
